import 'dart:convert';
import 'dart:io';

import 'package:connectivity/connectivity.dart';
import 'package:cps_app_network/src/interceptor/signature_interceptor.dart';
import 'package:dio/dio.dart';
import 'dart:developer' as developer;

import 'package:flutter/foundation.dart';

///***********************************************
///文件描述：
///创建人：dgg108698
///创建时间：2020/9/8
///修改人：
///修改时间：
///修改备注：
///***********************************************

class DggRequestApi {
  final String _scheme;
  final String _host;
  final int _port;

  DggRequestApi({String scheme = 'https', String host, int port})
      : _scheme = scheme,
        _host = host,
        _port = port;

  Future get(
    String path, {
    Map<String, String> query,
    Map<String, dynamic> header,
    Map<String, dynamic> data,
  }) =>
      _request(
        scheme: _scheme,
        host: _host,
        port: _port,
        method: 'GET',
        path: path,
        query: query,
        header: header,
        data: data,
      );

  Future post(
    String path, {
    Map<String, dynamic> header,
    Map<String, dynamic> body,
    List<MapEntry<String, File>> files,
  }) =>
      _request(
        scheme: _scheme,
        host: _host,
        port: _port,
        method: 'POST',
        path: path,
        header: header,
        body: body,
        files: files,
        data: null,
      );

  Future put(
    String path, {
    Map<String, dynamic> header,
    Map<String, dynamic> body,
    List<MapEntry<String, File>> files,
  }) =>
      _request(
        scheme: _scheme,
        host: _host,
        port: _port,
        method: 'PUT',
        path: path,
        header: header,
        body: body,
        files: files,
        data: null,
      );

  Future any(String path,
          {Map<String, dynamic> header,
          Map<String, dynamic> body,
          List<MapEntry<String, File>> files,
          Map<String, dynamic> data,
          String method}) =>
      _request(
        scheme: _scheme,
        host: _host,
        port: _port,
        method: method ?? 'POST',
        path: path,
        header: header,
        body: body,
        files: files,
        data: data,
      );


  ///下载文件
  Future downloadFile(String urlPath, String savePath,
      {ProgressCallback onReceiveProgress}) async {
    Response response =  await DggNetworkClient._initialGetDio.download(urlPath, savePath,
        onReceiveProgress: onReceiveProgress,
        options: Options(sendTimeout: 25000, receiveTimeout: 25000));
    return response;
  }



  Future anyOtherHost(String path,
      {String otherHost,
      Map<String, dynamic> header,
      Map<String, dynamic> param,
      String contentType,
      String method}) async {
    try {
      BaseOptions options = BaseOptions(
          receiveTimeout: 15000,
          connectTimeout: 15000,
          baseUrl: otherHost,
          headers: header,
          contentType: ContentType.parse(contentType).toString(),
          method: method);
      var url = Uri(
        path: path,
      ).toString();
      Dio dio = Dio(options);
      Response response = await dio.request(
        url,
        data: param,
      );
      return response.data;
    } catch (e) {}
  }

  Future _request({
    String scheme,
    String host,
    int port,
    String path,
    String method,
    Map<String, dynamic> body,
    Map<String, String> query,
    Map<String, dynamic> header,
    List<MapEntry<String, File>> files,
    dynamic data,
  }) async {
    //将qurey直接加入地址内

    String url;
    if (host == null) {
      assert(DggNetworkClient._initialGetDio.options.baseUrl != null);
      url = path;
    } else {
      url = Uri(
        scheme: scheme,
        host: host,
        port: port,
        path: path,
        queryParameters: query,
      ).toString();
    }

    if (data == null) {
      //当传入data时忽略其他参数
      if (method != 'GET') {
        //GET方法忽略data参数
        if (files != null) {
          var index = FormData.fromMap(body);
          files.forEach((item) {
            index.files.add(
              MapEntry(item.key, MultipartFile.fromFileSync(item.value.path)),
            );
          });
          data = index;
        } else {
          data = body;
        }
      }
    }

    Options options = Options(
      method: method,
      headers: header,
    );

    try {
      Response response = await DggNetworkClient._initialGetDio.request(
        url,
        data: data,
        options: options,
        queryParameters: host == null ? query : null,
      );
      return response.data;
      // if (response.statusCode == HttpStatus.ok) {
      //   return response.data;
      // } else {
      //   throw DggNetWorkError._(
      //     type: DggNetWorkError.request_error_status,
      //     error: StateError('${response.statusCode}'),
      //   );
      // }
    } on DioError catch (e) {
      Error error;
      switch (e.type) {
        case DioErrorType.CANCEL:
          error = DggNetWorkError._(
              type: DggNetWorkError.request_error_cancel,
              error: e.error,
              message: getErrorMsg(e.response));
          break;
        case DioErrorType.CONNECT_TIMEOUT:
        case DioErrorType.SEND_TIMEOUT:
        case DioErrorType.RECEIVE_TIMEOUT:
          error = DggNetWorkError._(
              type: DggNetWorkError.request_error_time_out,
              error: e.error,
              message: getErrorMsg(e.response));
          break;
        case DioErrorType.RESPONSE:
          if (-1 != e.response.data.toString().indexOf("用户未登录")) {
            ///用户未登陆跳转到登陆页面
            DggNetworkClient.loginOutCall.call();
          }
          return e.response.data;
        case DioErrorType.DEFAULT:
          var result = await Connectivity().checkConnectivity();
          if (result == ConnectivityResult.none) {
            error = DggNetWorkError._(
                type: DggNetWorkError.request_error_no_network,
                error: e.error,
                message: getErrorMsg(e.response));
          } else {
            error = DggNetWorkError._(
                type: DggNetWorkError.request_error_undefined,
                error: e.error,
                message: getErrorMsg(e.response));
          }
          break;
      }
      throw error;
    }
  }

  /// 获取错误的message
  String getErrorMsg(Response response) {
    if (null != response.data) {
      return response.data["message"];
    } else {
      return "失败!";
    }
  }
}

class DggNetworkClient {
  static Dio _dio;

  static Dio get _initialGetDio => _dio == null ? _dio = _build() : _dio;

  static Function loginOutCall;

  static Dio _build({DggNetWorkConfig config}) {
    BaseOptions options = BaseOptions(
      receiveTimeout: 15000,
      connectTimeout: 15000,
      baseUrl: config?.baseUrl,
      headers: config.header,
    );
    Dio dio = Dio(options);
    if (null != config.loginOut) {
      loginOutCall = config.loginOut;
    }
    dio.interceptors.add(
      PublicHeaderInterceptor(
        tokenGetter: config?.tokenGetter,
      ),
    );
    bool isLog = false;
    assert(isLog = true);
    if (isLog) {
      dio.interceptors.add(
        LogInterceptor(
          request: true,
          requestBody: true,
          requestHeader: true,
          responseHeader: true,
          responseBody: true,
          error: true,
          logPrint: (value) {
            developer.log(
              value == null
                  ? 'null'
                  : value is Map
                      ? json.encode(value)
                      : value.toString(),
              level: 600,
            );
          },
        ),
      );
    }
    return dio;
  }

  static void initConfig(DggNetWorkConfig config) {
    _dio = _build(config: config);
  }
}

class DggNetWorkConfig {
  final String baseUrl;
  final ValueGetter tokenGetter;
  final Map<String, dynamic> header;
  final Function loginOut;

  DggNetWorkConfig(
      {this.baseUrl, @required this.tokenGetter, this.header, this.loginOut});
}

class DggNetWorkError extends Error {
  static const int request_error_undefined = -1;

//请求状态错误
  static const int request_error_status = -2;

//请求取消
  static const int request_error_cancel = -3;

//请求超时
  static const int request_error_time_out = -4;

//无网络
  static const int request_error_no_network = -5;
  final int type;
  final dynamic _error;
  final String message;

  DggNetWorkError._({this.type, dynamic error, this.message}) : _error = error;

  @override
  String toString() {
    return _error?.toString() ?? super.toString();
  }

  @override
  StackTrace get stackTrace =>
      (_error is Error) ? _error.stackTrace : super.stackTrace;
}

class LogInterceptor extends Interceptor {
  LogInterceptor({
    this.request = true,
    this.requestHeader = true,
    this.requestBody = false,
    this.responseHeader = true,
    this.responseBody = false,
    this.error = true,
    this.logPrint = print,
  });

  /// Print request [Options]
  bool request;

  /// Print request header [Options.headers]
  bool requestHeader;

  /// Print request data [Options.data]
  bool requestBody;

  /// Print [Response.data]
  bool responseBody;

  /// Print [Response.headers]
  bool responseHeader;

  /// Print error message
  bool error;

  /// Log printer; defaults print log to console.
  /// In flutter, you'd better use debugPrint.
  /// you can also write log in a file, for example:
  ///```dart
  ///  var file=File("./log.txt");
  ///  var sink=file.openWrite();
  ///  dio.interceptors.add(LogInterceptor(logPrint: sink.writeln));
  ///  ...
  ///  await sink.close();
  ///```
  void Function(Object object) logPrint;

  @override
  Future onRequest(RequestOptions options) async {
    logPrint('*** Request ***');
    _printKV('uri', options.uri);

    if (request) {
      _printKV('method', options.method);
      _printKV('responseType', options.responseType?.toString());
      _printKV('followRedirects', options.followRedirects);
      _printKV('connectTimeout', options.connectTimeout);
      _printKV('receiveTimeout', options.receiveTimeout);
      _printKV('extra', options.extra);
    }
    if (requestHeader) {
      logPrint('headers:');
      options.headers.forEach((key, v) => _printKV(' $key', v));
    }
    if (requestBody) {
      logPrint('data:');
      _printAll(options.data);
    }
    logPrint('');
  }

  @override
  Future onError(DioError err) async {
    if (error) {
      logPrint('*** DioError ***:');
      logPrint('uri: ${err.request.uri}');
      logPrint('$err');
      if (err.response != null) {
        _printResponse(err.response);
      }
      logPrint('');
    }
  }

  @override
  Future onResponse(Response response) async {
    logPrint('*** Response ***');
    _printResponse(response);
  }

  void _printResponse(Response response) {
    _printKV('uri', response.request?.uri);
    if (responseHeader) {
      _printKV('statusCode', response.statusCode);
      if (response.isRedirect == true) {
        _printKV('redirect', response.realUri);
      }
      if (response.headers != null) {
        logPrint('headers:');
        response.headers.forEach((key, v) => _printKV(' $key', v.join(',')));
      }
    }
    if (responseBody) {
      logPrint('Response Text:');
      _printAll(response.toString());
    }
    logPrint('');
  }

  void _printKV(String key, Object v) {
    logPrint('$key: $v');
  }

  void _printAll(msg) {
    if (msg is Map) {
      logPrint(json.encode(msg));
    } else {
      msg.toString().split('\n').forEach(logPrint);
    }
  }
}
